#define HAVE_ECL_INPUT 1
#include <opm/input/eclipse/Parser/Parser.hpp>
#include <opm/material/fluidsystems/blackoilpvt/LiveOilPvt.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/Deck/DeckSection.hpp>
#include <opm/common/OpmLog/OpmLog.hpp>
#include <opm/input/eclipse/Units/Dimension.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/input/eclipse/Parser/ErrorGuard.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <iostream>
#include <unordered_map>

using namespace Opm;
using namespace std;

template <typename T>
void printVector(const std::vector<T> v, const int n)
{
    for (int i = 0; i < v.size(); ++i)
    {
        std::cout << std::setw(8) << std::left << v.at(i) << "    ";
        if (n != 0 && (i + 1) % n == 0)
            puts("");
    }
    std::cout << std::endl;
}

void printKeyword(std::vector<const Opm::DeckKeyword *> kwlist)
{
    puts("----------------------------------------------------------------");
    printf("Keyword List Size %ld\n", kwlist.size());
    for (const Opm::DeckKeyword *kw : kwlist)
    {

        printf("Record Size %ld\n", kw->size());
        for (int i = 0; i < kw->size(); i++)
        {
            auto records = kw->getRecord(i);
            printf("Item Size %ld\n", records.size());
            for (int j = 0; j < records.size(); j++)
            {
                auto item = records.getItem(j);
                std::cout << item.name() << " : " << item.data_size() << std::endl;
                std::cout << item << std::endl;
                // printVector(item.getData<double>(), 3);
            }
        }
    }
    puts("----------------------------------------------------------------\n");
}

double linear_interpolation(double x, double x1, double y1, double x2, double y2)
{
    return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
}

void linear_interpolation(const std::vector<double> &x, std::vector<double> &y, const std::vector<int> &index)
{
    if (index[0] == 0 && index.size() == x.size())
    {
        for (int i = 0; i < x.size(); i++)
        {
            y[i] = 0;
        }
        return;
    }

    if (index[0] == 1 && index.size() == x.size() - 1)
    {
        for (int j = 1; j < y.size(); j++)
        {
            y[j] = y[0];
        }
        return;
    }

    for (int i = 0; i < index.size(); i++)
    {
        int next_real_value_index = index[i] + 1;

        for (; next_real_value_index < x.size() && i + 1 < index.size(); next_real_value_index++)
        {
            for (int j = i + 1; j < index.size(); j++)
            {
                if (next_real_value_index != index[j])
                    break;
            }
        }

        if (next_real_value_index == -1)
        {
            for (int j = i + 1; j < y.size(); j++)
            {
                y[j] = y[index[i] - 1];
            }
            break;
        }
        y[index[i]] = linear_interpolation(x[index[i]], x[index[i] - 1], y[index[i] - 1], x[next_real_value_index], y[next_real_value_index]);
    }
}

// 线性插值函数
double linearInterpolation(double depth1, double pressure1, double depth2, double pressure2, double grid_depth)
{
    double delta_depth = depth2 - depth1;
    double delta_pressure = pressure2 - pressure1;
    double ratio = delta_pressure / delta_depth;
    double b = pressure1 - ratio * depth1;
    return ratio * grid_depth + b;
}

Opm::Deck convert(Opm::Deck deck)
{
    if (!deck.hasKeyword("PRVD"))
        return std::move(deck);
// #define linenum(x, y) ((x) * 2 + (y))

    std::vector<const Opm::DeckKeyword *> prvdlist = deck.getKeywordList("PRVD");
    std::vector<const Opm::DeckKeyword *> lowpermo = deck.getKeywordList("LOWPERMO");
    std::vector<const Opm::DeckKeyword *> PRELISDY = deck.getKeywordList("PRESSURE");
    std::vector<const Opm::DeckKeyword *> index = deck.getKeywordList("EQLDIMS");
    std::vector<const Opm::DeckKeyword *> dznet = deck.getKeywordList("DZNET");
    const_cast<Opm::Deck &>(deck).addKeyword(DeckKeyword(dznet.at(0)->location(),"NTG"));
    std::vector<double> ntg = {3,4,5};

    std::vector<Opm::Dimension> active_dimensions;
    std::vector<Opm::Dimension> default_dimensions;

    {
        std::vector<std::string> m_dimensions;
        auto active_unitsystem = deck.getActiveUnitSystem();
        auto default_unitsystem = deck.getDefaultUnitSystem();
        m_dimensions.push_back("1");
        for (const auto &dim_string : m_dimensions)
        {
            active_dimensions.push_back(active_unitsystem.getNewDimension(dim_string));
            default_dimensions.push_back(default_unitsystem.getNewDimension(dim_string));
        }
    }
    
    
    Opm::DeckKeyword keyword(dznet.at(0)->location(), "NTG");
    keyword.setDataKeyword(true);
    std::vector<Opm::DeckItem> items;
    Opm::DeckItem item("data", double(), active_dimensions, default_dimensions);
    for (int i = 0; i < ntg.size(); i++)
    {
        item.push_back(ntg[i]);
    }
    items.emplace_back(item);

    keyword.addRecord(Opm::DeckRecord{std::move(items), false});

    auto &tmp = const_cast<Deck&>(deck);
            for (auto it = tmp.begin(); it != tmp.end(); ++it)
            {
                if (it->name() == "NTG")
                {
                    *it = keyword;
                    break;
                }
            }
    const_cast<Opm::Deck &>(deck)=tmp;
    // const_cast<Opm::Deck &>(deck).addKeyword(DeckKeyword(dznet.at(0)->location(),"!d7?"));
    auto NYG = deck.hasKeyword("NTG");











    // auto tableindex = index.at(0)->getDataRecord().getItem(0).getData<int>()[0];

    // auto prvddata = prvdlist.at(0)->getRecord(tableindex - 1).getDataItem().getData<double>();
    // std::vector<double> pressure;
    // std::vector<double> depth;
    // for (int i = 0; i < prvddata.size(); i++)
    // {
    //     if (i % 2 == 0)
    //     {
    //         depth.push_back(prvddata[i]);
    //     }
    //     else
    //     {
    //         pressure.push_back(prvddata[i]);
    //     }
    // }
    // std::vector<double> grid_pressure;
    // int actn = 0;
    // std::vector<double> cell_depth = {3775.28875};
    // std::vector<int>  m_actnum = {1};
    // auto a =m_actnum.size();
    // for(int i:m_actnum){
    //     if(i==0){
    //         grid_pressure.push_back(0.0);
    //     }else{
        
    //     double interpolated_pressure = 0.0;
    //     if ((cell_depth[actn])< depth[0]) {
    //         interpolated_pressure = linearInterpolation(depth[0], pressure[0], depth[1], pressure[1], cell_depth[actn]);
    //         actn++;
    //     } else if (cell_depth[actn] > depth[depth.size() - 1]) {
    //         interpolated_pressure = linearInterpolation(depth[depth.size() - 2], pressure[pressure.size() - 2], depth[depth.size() - 1], pressure[pressure.size() - 1], cell_depth[actn]);
    //         actn++;
    //     } else {
    //         for (std::vector<double>::size_type i = 0; i < depth.size()-1; ++i) {
    //             if (cell_depth[actn] >= depth[i] && cell_depth[actn] <= depth[i+1]) {
    //                 interpolated_pressure = linearInterpolation(depth[i], pressure[i], depth[i+1], pressure[i+1], cell_depth[actn]);
    //                 break;
    //             }
    //         }
    //         actn++;
    //     }
    //     grid_pressure.push_back(interpolated_pressure);
    // }
    // }

    // std::vector<Opm::Dimension> active_dimensions;
    // std::vector<Opm::Dimension> default_dimensions;

    // {
    //     std::vector<std::string> m_dimensions;
    //     auto active_unitsystem = deck.getActiveUnitSystem();
    //     auto default_unitsystem = deck.getDefaultUnitSystem();
    //     m_dimensions.push_back("Pressure");
    //     for (const auto &dim_string : m_dimensions)
    //     {
    //         active_dimensions.push_back(active_unitsystem.getNewDimension(dim_string));
    //         default_dimensions.push_back(default_unitsystem.getNewDimension(dim_string));
    //     }
    // }

    // auto prvd = prvdlist.at(0);
    // for (int ntab = 0; ntab < prvd->size();) {
    //     auto dataRecord = prvd->getRecord(ntab);
    //     auto dataItem = dataRecord.getDataItem();
    //     auto raw_data = dataItem.getData<double>();

    //     int ncols = 2;
    //     int nrows = raw_data.size() / ncols;

    //     std::vector<std::vector<double>> data(2);
    //     std::unordered_map<int, std::vector<int>> default_data;
    //     for (int nr = 0; nr < nrows; nr++) {
    //         for (int i = 0; i < 2; i++) {
    //             data[i].push_back(raw_data[linenum(nr, i)]);
    //             if (raw_data[linenum(nr, i)]==0) default_data[i].push_back(nr);//my conditon
    //         }
    //     }

    //     for (auto [col, index] : default_data) {
    //         if (index.size() == 0) continue;
    //         // Pbub default data is none so we can use it
    //         linear_interpolation(data[std::abs(col - 1)], data[col], index);
    //      }

    //      if (++ntab == prvd->size()) break;
    // }

    // Opm::DeckKeyword keyword(prvdlist.at(0)->location(), "PRESSURE");
    // keyword.setDataKeyword(false);
    // std::vector<Opm::DeckItem> items;
    // Opm::DeckItem item("data", double(), active_dimensions, default_dimensions);
    // for (int i = 0; i < pressure.size(); i++)
    // {
    //     item.push_back(pressure[i]);
    // }
    // items.emplace_back(item);

    // keyword.addRecord(Opm::DeckRecord{std::move(items), false});

    // for (auto it = deck.begin(); it != deck.end(); ++it)
    // {
    //     if (it->name() == "PRVD")
    //     {
    //         *it = keyword;
    //         break;
    //     }
    // }

    return std::move(deck);
}

void func(std::string filename)
{
    Opm::Parser parser;
    auto raw_deck = parser.parseFile(filename);

    auto deck = convert(raw_deck);

    printKeyword(deck.getKeywordList("NTG"));

    TableManager tm(deck);
    cout << tm.getPvtoTables().size() << endl;
}

int main()
{
    Opm::OpmLog::setupSimpleDefaultLogging();
    std::string prefix("//home/opm/DATA/test/test.DATA");
    func(prefix);
    return 0;
}
